package smallfrench.moteur.biblio;

import java.math.BigDecimal;

import smallfrench.moteur.biblio.err.ErreurMath;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.RException;

/**
 * Un nombre flottant immuable.
 * @author antoine1023
 *
 */
public class RFlottant extends RObjet {

	public final double valeur;

	public RFlottant(double valeur) {
		super(RClasseFlottant.CLASSE);
		this.valeur = valeur;
	}

	//-------------------------------------------

	public float getValeurFloat() {
		return (float) valeur;
	}

	@Override
	public boolean equals(Object objet) {
		if (objet instanceof RFlottant) {
			RFlottant t = (RFlottant)objet;
			return t.valeur == valeur;
		}
		return false;
	}

	//------------------------------------------- Opérateurs binaires

	@Override
	public RObjet operateurPlusBase(RObjet o) {

		if (o instanceof REntier) {
			return new RFlottant(valeur + ((REntier)o).valeur);

		} else if (o instanceof RFlottant) {
			return new RFlottant(valeur + ((RFlottant)o).valeur);

		} else if (o instanceof RChaine) {
			return new RChaine(flottantEnChaine() + ((RChaine)o).valeur);

		} else if (o instanceof RDecimal) {
			// Ca marche parce que a + b == b + a
			return ((RDecimal) o).operateurPlusBase(this);
		}
		return null;
	}

	@Override
	public RObjet operateurMoinsBase(RObjet o) {

		if (o instanceof REntier) {
			return new RFlottant(valeur - ((REntier)o).valeur);

		} else if (o instanceof RFlottant) {
			return new RFlottant(valeur - ((RFlottant)o).valeur);

		} else if (o instanceof RDecimal) {
			return ((RDecimal) o).operateurMoinsBase(this);
		}
		return null;
	}

	@Override
	public RObjet operateurEtoileBase(RObjet o) {

		if (o instanceof REntier) {
			return new RFlottant(valeur * ((REntier)o).valeur);

		} else if (o instanceof RFlottant) {
			return new RFlottant(valeur * ((RFlottant)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return new RDecimal(new BigDecimal(valeur)
			.multiply(bd, RDecimal.MATH_CONTEXT));
		}
		return null;
	}

	@Override
	public RObjet operateurSlashBase(RObjet o) throws RException {

		if (o instanceof REntier) {
			long v = ((REntier)o).valeur;
			if (v == 0L)
				throw ErreurMath.nouvelleDivisionPar0();
			return new RFlottant(valeur / v);

		} else if (o instanceof RFlottant) {
			double v = ((RFlottant)o).valeur;
			if (v == 0L)
				throw ErreurMath.nouvelleDivisionPar0();
			return new RFlottant(valeur / v);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			if (bd.compareTo(BigDecimal.ZERO) == 0)
				throw ErreurMath.nouvelleDivisionPar0();

			return new RDecimal(new BigDecimal(valeur)
			.divide(bd, RDecimal.MATH_CONTEXT));

		}
		return null;
	}

	@Override
	public RObjet operateurModuloBase(RObjet o) throws RException {

		if (o instanceof REntier) {
			long v = ((REntier)o).valeur;
			if (v == 0L)
				throw ErreurMath.nouvelleModuloPar0();
			return new RFlottant(valeur % v);

		} else if (o instanceof RFlottant) {
			double v = ((RFlottant)o).valeur;
			if (v == 0L)
				throw ErreurMath.nouvelleModuloPar0();
			return new RFlottant(valeur % v);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			if (bd.compareTo(BigDecimal.ZERO) == 0)
				throw ErreurMath.nouvelleModuloPar0();

			return new RDecimal(new BigDecimal(valeur)
			.remainder(bd, RDecimal.MATH_CONTEXT));

		}
		return null;
	}

	//----------------------------------------- comparaisons

	@Override
	public RBooleen operateurEgalBase(RObjet o) {

		if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur == ((RFlottant)o).valeur);

		}
		return null;
	}

	@Override
	public RBooleen operateurInegalBase(RObjet o) {

		if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur != ((RFlottant)o).valeur);

		}
		return null;
	}

	@Override
	public RBooleen operateurSupBase(RObjet o) {

		if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur > ((RFlottant)o).valeur);

		} else if (o instanceof REntier) {
			return RBooleen.nouveau(valeur > ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) == 1);
		}
		return null;
	}

	@Override
	public RBooleen operateurInfBase(RObjet o) {

		if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur < ((RFlottant)o).valeur);

		} else if (o instanceof REntier) {
			return RBooleen.nouveau(valeur < ((REntier)o).valeur);

		} else if (o instanceof RDecimal) {
			BigDecimal bd = ((RDecimal)o).valeur;
			return RBooleen.nouveau(new BigDecimal(valeur)
			.compareTo(bd) == -1);
		}
		return null;
	}

	@Override
	public RBooleen operateurSupegalBase(RObjet o) {
		if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur >= ((RFlottant)o).valeur);
		}
		return null;
	}

	@Override
	public RBooleen operateurInfegalBase(RObjet o) {
		if (o instanceof RFlottant) {
			return RBooleen.nouveau(valeur <= ((RFlottant)o).valeur);
		}
		return null;
	}

	//------------------------------------------- Opérateurs unaires

	@Override
	public RObjet operateurMoinsUnaire()
			throws RException{
		return new RFlottant(- valeur);
	}

	//------------------------------------------- Membres

	@Override
	public RObjet getMembre(String nom) {
		return RClasseFlottant.CLASSE.champs.get(nom);
	}

	//------------------------------------------- Strings

	public String getNom() {
		return "Flottant";
	}

	public String flottantEnChaine() {

		if (valeur == Float.POSITIVE_INFINITY)
			return "+infini";
		else if (valeur == Float.NEGATIVE_INFINITY)
			return "-infini";
		else if (valeur == Float.NaN)
			return "interdit";

		// N’oublions pas qu’en France, nous utilisons une virgule avant les
		// décimales
		return String.valueOf(valeur).replace('.', ',');
	}

	@Override
	public RChaine enChaine() {
		return new RChaine(flottantEnChaine());
	}

}
